﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace ProgramCat.FiddlerExtension.Core.Utils.Crypto
{
    /// <summary>
    /// Md2 4 5 校验
    /// </summary>
    public class HashCrypt : Singleton<HashCrypt>
    {

        private HashCrypt()
        {

        }
        public string GetMD4(string textToHash)
        {
            // get padded uints from bytes
            List<byte> bytes = EncodedHelp.GetEncoding(EncodedHelp.EncodingType.GBK).GetBytes(textToHash).ToList();
            uint bitCount = (uint)(bytes.Count) * 8;
            bytes.Add(128);
            while (bytes.Count % 64 != 56) bytes.Add(0);
            var uints = new List<uint>();
            for (int i = 0; i + 3 < bytes.Count; i += 4)
                uints.Add(bytes[i] | (uint)bytes[i + 1] << 8 | (uint)bytes[i + 2] << 16 | (uint)bytes[i + 3] << 24);
            uints.Add(bitCount);
            uints.Add(0);

            // run rounds
            uint a = 0x67452301, b = 0xefcdab89, c = 0x98badcfe, d = 0x10325476;
            Func<uint, uint, uint> rol = (x, y) => x << (int)y | x >> 32 - (int)y;
            for (int q = 0; q + 15 < uints.Count; q += 16)
            {
                var chunk = uints.GetRange(q, 16);
                uint aa = a, bb = b, cc = c, dd = d;
                Action<Func<uint, uint, uint, uint>, uint[]> round = (f, y) =>
                {
                    foreach (uint i in new[] { y[0], y[1], y[2], y[3] })
                    {
                        a = rol(a + f(b, c, d) + chunk[(int)(i + y[4])] + y[12], y[8]);
                        d = rol(d + f(a, b, c) + chunk[(int)(i + y[5])] + y[12], y[9]);
                        c = rol(c + f(d, a, b) + chunk[(int)(i + y[6])] + y[12], y[10]);
                        b = rol(b + f(c, d, a) + chunk[(int)(i + y[7])] + y[12], y[11]);
                    }
                };
                round((x, y, z) => (x & y) | (~x & z), new uint[] { 0, 4, 8, 12, 0, 1, 2, 3, 3, 7, 11, 19, 0 });
                round((x, y, z) => (x & y) | (x & z) | (y & z), new uint[] { 0, 1, 2, 3, 0, 4, 8, 12, 3, 5, 9, 13, 0x5a827999 });
                round((x, y, z) => x ^ y ^ z, new uint[] { 0, 2, 1, 3, 0, 8, 4, 12, 3, 9, 11, 15, 0x6ed9eba1 });
                a += aa; b += bb; c += cc; d += dd;
            }

            // return hex encoded string
            byte[] outBytes = new[] { a, b, c, d }.SelectMany(BitConverter.GetBytes).ToArray();
            return BitConverter.ToString(outBytes).Replace("-", "").ToLower();
        }

        public string GetMD5(string textToHash)
        {
            
            MD5 md5Hash = MD5.Create();
            byte[] data = md5Hash.ComputeHash(EncodedHelp.GetEncoding(EncodedHelp.EncodingType.GBK).GetBytes(textToHash));
            return BytesToHash(data);
        }
        public string GetSHA1(string textToHash)
        {

            SHA1 sha1Hash = SHA1.Create();
            byte[] data = sha1Hash.ComputeHash(EncodedHelp.GetEncoding(EncodedHelp.EncodingType.GBK).GetBytes(textToHash));
            return BytesToHash(data);
        }
        public string GetSHA256(string textToHash)
        {
            SHA256 sha256Hash = SHA256.Create();
            byte[] data = sha256Hash.ComputeHash(EncodedHelp.GetEncoding(EncodedHelp.EncodingType.GBK).GetBytes(textToHash));
            return BytesToHash(data);
        }
        public string GetSHA512(string textToHash)
        {
            SHA512 sha512Hash = SHA512.Create();
            byte[] data = sha512Hash.ComputeHash(EncodedHelp.GetEncoding(EncodedHelp.EncodingType.GBK).GetBytes(textToHash));
            return BytesToHash(data);
        }


        static protected ulong[] Crc32Table;
        private void GetCRC32Table()
        {
            ulong Crc;
            Crc32Table = new ulong[256];
            int i, j;
            for (i = 0; i < 256; i++)
            {
                Crc = (ulong)i;
                for (j = 8; j > 0; j--)
                {
                    if ((Crc & 1) == 1)
                        Crc = (Crc >> 1) ^ 0xEDB88320;
                    else
                        Crc >>= 1;
                }
                Crc32Table[i] = Crc;
            }
        }
        public string GetCRC(string textToHash)
        {
            //生成码表
            GetCRC32Table();
            byte[] buffer = System.Text.ASCIIEncoding.Default.GetBytes(textToHash); 
            ulong value = 0xffffffff;
            int len = buffer.Length;
            for (int i = 0; i < len; i++)
            {
                value = (value >> 8) ^ Crc32Table[(value & 0xFF) ^ buffer[i]];
            }
            return (value ^ 0xffffffff).ToString("X2");

        }

        private string BytesToHash(byte[] data)
        {
            StringBuilder sBuilder = new StringBuilder();

            for (int i = 0; i < data.Length; i++)
                sBuilder.Append(data[i].ToString("x2"));
           
            return sBuilder.ToString();
        }
    }
}
